package org.alis.rpc.common.util;

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.alis.rpc.common.annotation.ThreadSafe;

import javax.annotation.Nullable;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ThreadPoolUtils desc:
 *
 * @author luochuan
 * @date 2020/9/30
 */
@UtilityClass
@Slf4j
@ThreadSafe
public class ThreadPoolUtils {
    /**
     * 线程池维护线程最小数量
     */
    private static final int SIZE_CORE_POOL = 4;
    /**
     * 获取当前系统配置的cpu处理线程数
     * 线程池最大维护数量
     */
    private static final int SIZE_MAX_POOL = Runtime.getRuntime().availableProcessors() * 2 + 1;
    /**
     * 存活时间 1分钟
     */
    private static final int MAX_KEEP_ALIVE = 1;
    /**
     * 队列最大长度
     */
    private static final int MAX_QUEUE_SIZE = 1000;

    /**
     * 获取超时
     * 给三十秒宽容宽容
     */
    private static final int TIME_OUT_MILL = 30 * 1000;

    /**
     * 阻塞队列
     */
    private static final BlockingQueue<Runnable> RUNNABLE_QUEUE = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE);

    /**
     * 拒绝策略
     */
    private static final RejectedExecutionHandler REJECTED_EXECUTION_HANDLER = new ThreadRejectedExecutionHandler();

    /**
     * 使用枚举单例创建
     */
    private enum ThreadPoolEnum {
        /**
         * 枚举实例
         */
        INSTANCE;
        private ThreadPoolExecutor executor;

        ThreadPoolEnum() {
            executor = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, MAX_KEEP_ALIVE, TimeUnit.MINUTES, RUNNABLE_QUEUE, NameThreadFactory.build("线程池工具线程"), REJECTED_EXECUTION_HANDLER);
        }

        /**
         * 获取单例实例
         *
         * @return 线程池的单例实例
         */
        public ThreadPoolExecutor getThreadPoolInstance() {
            return executor;
        }
    }

    /**
     * 定时任务线程池
     */
    private enum ScheduledThreadPoolEnum {
        /**
         * 枚举实例
         */
        INSTANCE;
        private ScheduledExecutorService executor;

        ScheduledThreadPoolEnum() {
            executor = new ScheduledThreadPoolExecutor(SIZE_CORE_POOL, NameThreadFactory.build("alis-rpc-thread-pool"), REJECTED_EXECUTION_HANDLER);
        }

        /**
         * 获取单例实例
         *
         * @return 线程池的单例实例
         */
        public ScheduledExecutorService getScheduledThreadPoolInstance() {
            return executor;
        }
    }

    /**
     * 执行任务
     *
     * @param runnable 线程
     */
    public static void execute(Runnable runnable) {
        getInstance().execute(runnable);
    }

    /**
     * 返回future对象
     *
     * @param callable 可回调对象
     * @return future 预期对象
     */
    public static <T> Future<T> submit(Callable<T> callable) {
        return getInstance().submit(callable);
    }


    /**
     * 获取具有返回结果的数据
     *
     * @param future 可被回调的future线程
     * @param <T>    参数类型
     * @return 返回结果
     */
    public static <T> T backResult(Future<T> future) {
        try {
            return future.get(TIME_OUT_MILL, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            //线程终止异常
            log.error("获取返回任务返回时线程异常停止，异常信息", e);
            Thread.currentThread().interrupt();
        } catch (ExecutionException | TimeoutException e) {
            //执行异常 // 操作异常
            log.error("获取线程操作失败，异常信息", e);
        }
        return null;

    }

    /**
     * 线程池线程是否为空
     *
     * @return boolean 返回结果
     */
    public static boolean isEmpty() {
        return RUNNABLE_QUEUE.isEmpty();
    }

    /**
     * 停止两个线程池
     */
    public static void shutdown(){
        getInstance().shutdown();
        getScheduledInstance().shutdown();
    }

    /**
     * 发布定时任务
     *
     * @param runnable 线程任务
     * @param period   间隔时间
     * @param timeUnit 时间工具
     */
    public static void scheduleTask(Runnable runnable, Long period, TimeUnit timeUnit) {
        scheduleTask(runnable, 0L, period, timeUnit);
    }

    /**
     * 发布定时任务
     *
     * @param runnable 线程任务
     * @param period   间隔时间
     * @param timeUnit 时间工具
     * @param delay    延迟发布
     */
    public static void scheduleTask(Runnable runnable, Long delay, Long period, TimeUnit timeUnit) {
        ScheduledExecutorService scheduledService = getScheduledInstance();
        scheduledService.scheduleAtFixedRate(runnable, delay, period, timeUnit);
    }


    /**
     * 发布定时任务
     *
     * @param runnable 线程任务
     * @param timeUnit 时间工具
     * @param delay    延迟发布
     */
    public static void schedule(Runnable runnable, Long delay, TimeUnit timeUnit) {
        ScheduledExecutorService scheduledService = getScheduledInstance();
        scheduledService.schedule(runnable, delay, timeUnit);
    }


    /**
     * 获取实例
     *
     * @return 线程池的单例实例
     */
    private static ThreadPoolExecutor getInstance() {
        return ThreadPoolEnum.INSTANCE.getThreadPoolInstance();
    }

    /**
     * 获取定时任务线程
     *
     * @return 定时任务线程池任务呢
     */
    private static ScheduledExecutorService getScheduledInstance() {
        return ScheduledThreadPoolEnum.INSTANCE.getScheduledThreadPoolInstance();
    }

    public static class NameThreadFactory implements ThreadFactory {

        private static final AtomicInteger POOL_SEQ = new AtomicInteger(1);
        private final AtomicInteger mThreadName = new AtomicInteger(1);

        private final String mPrefix;
        private final boolean mDaemo;

        /**
         * 空串构造 默认线程名
         */
        public NameThreadFactory() {
            this("pool-" + POOL_SEQ.getAndIncrement(), false);
        }

        /**
         * 带自定义名称的构造
         *
         * @param prefix 前缀
         */
        private NameThreadFactory(String prefix) {
            this(prefix, false);
        }

        /**
         * 核心构造
         *
         * @param prefix 前缀
         * @param daemo  是否守护线程
         */
        private NameThreadFactory(String prefix, boolean daemo) {
            mPrefix = prefix + "-task-";
            mDaemo = daemo;
        }

        /**
         * 线程池核心工厂
         *
         * @param runnable 线程接口
         * @return 线程
         */
        @Override
        public Thread newThread(@Nullable Runnable runnable) {
            String name = mPrefix + mThreadName.getAndIncrement();
            Thread ret = new Thread(runnable, name);
            ret.setDaemon(mDaemo);
            return ret;
        }

        /**
         * 静态构建方法
         * new的操作全在本类实现
         *
         * @param prefix 前缀
         */
        public static NameThreadFactory build(String prefix) {
            return new NameThreadFactory(prefix);
        }
    }

    /**
     * 线程拒绝策略
     */
    public static class ThreadRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                executor.getQueue().put(r);
            } catch (InterruptedException e) {
                log.error("重新添加任务时候线程异常，异常堆栈->", e);
                Thread.currentThread().interrupt();
            }
        }
    }

}
